package com.snap.vseries.analyze;

import com.snap.vseries.bean.ChannelBean;

import org.apache.commons.math3.fitting.PolynomialCurveFitter;
import org.apache.commons.math3.fitting.WeightedObservedPoints;

import java.util.List;

public class RelativeNormal {

    public static void doAnalyze(List<ChannelBean> channelBeans, AlgoParams algoParams) {
        double relativeBLOptThreshold = algoParams.getRelativeBLOptThreshold();
        boolean relativeBLOpt = algoParams.isRelativeBLOpt();
        for (int i = 0; i < channelBeans.size(); i++) {
            ChannelBean bean = channelBeans.get(i);
            int start = bean.getBlStart() - 1;
            int len = bean.getBlStop() - start;
            float[] signal = bean.getOrigin();
            if (signal == null || signal.length == 0
                    || start < 0 || len < 2 || start + len > signal.length) continue;

            //基线长度还算可以
            if (len > 4) {
                double[] lineParam = lineFit(signal, start, len);

                //减去斜率  除以倍数
                float[] delta = new float[signal.length];
                for (int j = 0; j < delta.length; j++) {
                    delta[j] = (float) ((signal[j] - lineParam[1] * (j + 1)) / lineParam[0]);
                }

                //ma在此还有以20取模的手法（仅大于20时） 等效约1.2% 或1%
                if (relativeBLOpt) {
                    for (int j = 0; j < bean.getBlStop() - 1; j++) {
                        if (Math.abs(delta[j] - 1) > relativeBLOptThreshold) {
                            delta[j] -= 1f;
                            delta[j] %= relativeBLOptThreshold;
                            delta[j] += 1f;
                        }
                    }
                }

                //移动平滑
                for (int j = 2; j < delta.length - 2; j++) {
                    delta[j] = (delta[j - 2] + delta[j - 1] + delta[j + 1] + delta[j + 2]) / 4.0F;
                }

                bean.setFit(delta);
            } else {//长度为2 3 4的时候
                double sum = 0;
                for (int j = 0; j < len; j++) {
                    sum += signal[j + start];
                }
                sum /= len;

                float[] delta = new float[signal.length];
                for (int j = 0; j < delta.length; j++) {
                    delta[j] = (float) (signal[j] / sum);
                }
                bean.setFit(delta);
            }
        }
    }

    private static double[] lineFit(float[] signal, int start, int len) {
        WeightedObservedPoints points = new WeightedObservedPoints();
        for (int i = 0; i < len; i++) {
            points.add(i + start + 1, signal[i + start]);
        }
        PolynomialCurveFitter fitter = PolynomialCurveFitter.create(1);
        return fitter.fit(points.toList());
    }
}
